/*
 * Copyright 2007 Jeffrey Palm
 */

import java.util.*;

class World {

  private final Map<String,Type> names2types = new HashMap<String,Type>();
  
  // --------------------------------------------------
  // Interface
  // --------------------------------------------------

  /* Interfaces */

  public final static class Mods {
    public final static int SYNTHETIC = 0x1;
    public final static int MUTABLE   = 0x2;
  }

  public interface Thing {
    World getWorld();
  }

  public interface Type extends Thing {
    String getName();
    String getSupertype();
    List<Decl> getDecls();
  }

  public interface Decl extends Thing {
    String getType();
    String getName();
    int getModifiers();
    List<Param> getParams();
    
    interface Mutable extends Decl {
      void addParam(String type, String name);
    }
  }

  public interface Param extends Thing {
    String getType();
    String getName();
  }


  /* Methods */

  public Collection<Type> getTypes() {
    return names2types.values();
  }

  Decl.Mutable addDecl(Type t, String type, String name, int mods) {
    return (Decl.Mutable)((TypeImpl)t).addDecl(type,name,mods);
  }

  public Type getType(String type) {
    return addType(type,null);
  }

  Type addType(String type, String superType) {
    Type res = names2types.get(type);
    if (res == null) names2types.put(type, res = new TypeImpl(this,type,superType));
    return res;
  }

  public Type setSuper(String type, String superType) {
    TypeImpl t = (TypeImpl)getType(type);
    t.setSuper(superType);
    return t;
  }

  public String toString() {
    StringBuffer res = new StringBuffer();
    for (Type t : names2types.values()) {
      res.append(t.getName()).append(" : ").append(t.getSupertype()).append(" {\n");
      for (Decl d : t.getDecls()) {
        res.append("  ");
        if ((d.getModifiers() & Mods.MUTABLE) != 0) res.append("+ ");
        if ((d.getModifiers() & Mods.SYNTHETIC) != 0) res.append("- ");
        res.append(d.getType()).append(" ").append(d.getName());
        List<Param> ps = d.getParams();
        if (ps != null && !ps.isEmpty()) {
          res.append("(");
          boolean first = true;
          for (Param p : ps) {
            if (!first) res.append(",");
            first = false;
            if (p.getType() != null) {
              res.append(p.getType()).append(" ");
            }
            res.append(p.getName());
          }
          res.append(")");
        }
        res.append('\n');
      }
      res.append("}\n");
      res.append('\n');
    }
    return res.toString();
  }

  // --------------------------------------------------
  // Private
  // --------------------------------------------------

  private abstract static class ThingImpl implements Thing {
    private final World world;
    ThingImpl(World world) {this.world = world;}
    public final World getWorld() {return world;}
  }

  private final static class TypeImpl extends ThingImpl implements Type {

    private final String name;
    private String superName;
    private final List<Decl> decls = new ArrayList<Decl>();

    TypeImpl(World w, String name, String superName) {
        super(w);
        this.name = name;
        this.superName = superName;
    }

    Decl addDecl(String type, String name, int mods) {
      Decl res = new DeclImpl(getWorld(),type,name,mods);
      decls.add(res);
      return res;
    }

    public void setSuper(String superName) {
      this.superName = superName;
    }

    public String getSupertype() {
      return superName;
    }

    public List<Decl> getDecls() {
      return decls;
    }

    public String getName() {
      return name;
    }

  }

  private final static class DeclImpl extends ThingImpl implements Decl.Mutable {
    
    private final String type;
    private final String name;
    private final int mods;
    private final List<Param> params = new ArrayList<Param>();
    
    DeclImpl(World w, String type, String name, int mods) {
      super(w);
      this.type = type;
      this.name = name;
      this.mods = mods;
    }
    
    public String getType() {return type;}
    public String getName() {return name;}
    public int getModifiers() {return mods;}
    public List<Param> getParams() {return params;}

    public void addParam(String type, String name) {
      params.add(new ParamImpl(getWorld(),type,name));
    }
    
    public int hashCode() {
      return getType().hashCode()<<16 + getName().hashCode();
    }
    
    public boolean equals(Object o) {
      if (!(o instanceof Decl)) return false;
      Decl that = (Decl)o;
      return this.getType().equals(that.getType())
        &&   this.getName().equals(that.getName());
    }
  }

  private final static class ParamImpl extends ThingImpl implements Param {
    
    private final String type;
    private final String name;
    
    ParamImpl(World w, String type, String name) {
      super(w);
      this.type = type;
      this.name = name;
    }
    
    public String getType() {return type;}
    public String getName() {return name;}
    
    public int hashCode() {
      return getType().hashCode()<<16 + getName().hashCode();
    }
    
    public boolean equals(Object o) {
      if (!(o instanceof Param)) return false;
      Decl that = (Decl)o;
      return this.getType().equals(that.getType())
        &&   this.getName().equals(that.getName());
    }
  }
  
}